using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public unsafe partial struct LStreamReader
    {
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void UnsafeReadUnmanagedStruct<T>(LSerializeContext* context, ref T value) where T : IEquatable<T>
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                // Log.Info($"Read unmanaged struct unsafe start: {buffer->Remain} {typeof(T).Name}");
                var origin = buffer;
                buffer = span.Buffer;
                buffer->Seek(0, SeekOrigin.Begin);
                buffer->HandleUnmanagedStruct(out RefHeader* header);
                context->IncreaseDepth();
                UnsafeReadUnmanagedValue(ref value);
                context->DecreaseDepth();
                buffer = origin;
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
                // Log.Info($"Read unmanaged struct unsafe done: {value}");
            }
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadUnmanagedStruct<T>(LSerializeContext* context, ref T value) where T : unmanaged, IEquatable<T>
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                // Log.Info($"Read unmanaged struct start: {buffer->Remain} {typeof(T).Name}");
                var origin = buffer;
                buffer = span.Buffer;
                buffer->HandleUnmanagedStruct(out RefHeader* header);
                // int size = Unsafe.SizeOf<T>();
                context->IncreaseDepth();
                ReadUnmanagedValue(ref value);
                context->DecreaseDepth();
                buffer = origin;
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
                // Log.Info($"Read unmanaged struct done: {value}");
            }
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadUnmanagedStruct<T>(LSerializeContext* context, ref T? value) where T : unmanaged
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                // Log.Info($"Read unmanaged struct start: {buffer->Remain} {typeof(T).Name}");
                var origin = buffer;
                buffer = span.Buffer;
                buffer->HandleUnmanagedStruct(out RefHeader* header);
                // int size = Unsafe.SizeOf<T>();
                context->IncreaseDepth();
                ReadUnmanagedValue(ref value);
                context->DecreaseDepth();
                buffer = origin;
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
                // Log.Info($"Read unmanaged struct done: {value}");
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadManagedStruct<T>(LSerializeContext* context, ref T value)
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                // Log.Info($"Read managed struct start: {buffer->Remain} {typeof(T).Name}");
                var origin = buffer;
                buffer = span.Buffer;
                HandleUnmanagedStruct(out RefHeader* header);
                context->IncreaseDepth();
                BuildBufferSpanCache();
                // GetFormatter<T>().Deserialize(context, Ptr, ref value);
                ReadRealTypeValue(context, ref value);
                context->DecreaseDepth();
                buffer = origin;
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
                // Log.Info($"Read managed struct done: {value}");
            } 
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)] 
        public void ReadStruct<T>(LSerializeContext* context, ref T value) where T : IEquatable<T>
        {
            bool isUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<T>();
            if (isUnmanaged)
                UnsafeReadUnmanagedStruct(context, ref value);
            else
                ReadManagedStruct(context, ref value);
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadReference<T>(LSerializeContext* context, ref T value)
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                // Log.Info($"Read reference start: {span.Buffer->Remain} {typeof(T).Name}");
                var origin = buffer;
                buffer = span.Buffer;
                HandleUnmanagedStruct(out RefHeader* header);
                context->IncreaseDepth();
                BuildBufferSpanCache();
                // GetFormatter<T>().Deserialize(context, Ptr, ref value);
                ReadRealTypeValue(context, ref value);
                context->DecreaseDepth();
                buffer = origin;
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
                // Log.Info($"Read reference done: {value}");
            } 
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadRealTypeValue<T>(LSerializeContext* context, ref T value)
        {
            Type type = null;
            GetFormatter<Type>().Deserialize(context, Ptr, ref type);
            if (null! == type) 
            {
                value = default!;
                return;
            }
            
            if (type.IsSubclassOf(typeof(T)) || typeof(T).IsAssignableFrom(type))
            {
                object obj = null;
                GetFormatter(type).Deserialize(context, Ptr, ref obj);
                value = (T) obj;
            }
            else
            {
                GetFormatter<T>().Deserialize(context, Ptr, ref value);
            }
        }
    }
}